import Color.*
import com.sun.org.apache.regexp.internal.RE
import java.io.BufferedReader
import java.lang.NumberFormatException
import java.util.*
import kotlin.collections.AbstractCollection

/**
 * kotlin翻译
 * fun 代表一个方法 用来声明一个函数
 * main 是方法名
 * args: Array<String> 参数 java中是类型先,变量名后 ,kotlin正相反,先声明变量名,再声明类型
 * Array表示一个数组,在kotlin中没有[]概念,也类似于集合,在kotlin中,数组和集合基本没什么区别
 * println 替代了System.out.println 是对它的一个封装
 */
fun main(args: Array<String>): Unit {
//    println("hello,world")
//    println(max(3, 4))
//    println("Hello,$name!")
//    println("1 + 2 = ${1 + 2}")
//    println("a  ${if (a >= 10) "大于等于10" else "小于10"} ")
//    //${}  if (a >= 10)
//    println(
//        person.name//相当于get方法
//    )
//    val rectangle = Rectangle(10, 10)
//    println(rectangle.isSquare)
//    println(rectangle.height)
//    println(rectangle.width)
//    println(
//        WHITE.rgb()
//    )
//    println(getColorStr(WHITE))
//    println(mix(WHITE, YELLOW))
//    mix3()
//    play()
//    println(isLetter('a'))
//    println(check('1'))
//    println(set.javaClass)
//    println(list.javaClass)
//    println(map.javaClass)
//    println(strings.last())
//    println(numbers.max())
//    println(list)
//    println(joinToString(list, ";", "(", ")"))
//    //命名参数
//    //当调用一个kotlin定义的方法时,可以显示表名一些参数的名称,为了避免混淆,如果使用了一个命名参数,其他的就都需要使用
//    println(joinToString(postfix = "}", prefix = "{", collection = list, separator = "|"))
//    //参数可以有默认值,当定义默认值之后,应用的方法可以不填有默认值的参数,如果填了,则会优先替换默认值
//    println(joinToString(list))
//    println(joinToString(list, "&", "|", "|"))
    println("Kotlin".lastChar123())
}

/**
 * 方法后的: Int代表当前方法的返回值
 * 所有的kotlin的方法都有返回值 无返回值的方法返回的是一个:Unit,类似Java中的void,一般可以不写
 */
//fun max(a: Int, b: Int): Int {
//    return if (a > b) a else b
//}

/**
 * 在kotlin中,所有的方法都是一个表达式,也就是可以用=来表示两边赋值,可以用来代替return
 */
fun max(a: Int, b: Int): Int = if (a > b) a else b

/**
 * 可变变量和不可变变量:
 * val (value) -不可变引用,在初始化后不能再次赋值,对应Java中的final修饰符
 * var (variable) -可变引用,这种变量的值可以被改变,对应Java中的普通变量
 * var 被初始化之后是什么类型,赋值就必须是什么类型,不能一个int类型赋值String类型
 */
val str: String = "这是一个String"
var a = 9

/**
 * 字符串模板
 * 这是kotlin的一个新特性,声明一个变量name,并且在后面的字符串字面值中使用了他,只需要在前面加$符号
 * 在java中必须"hello," + name + "!"这么写,很啰嗦
 */
val name = "LC"

/**
 * 类和属性
 * 在kotlin中不需要写new,只要是大写的字母开头一般都代表一个构造方法,类型也可以不指定
 * 用var声明一个类的属性时,可以通过set来赋值,使用val不能直接赋值,但是可以在构造中赋值
 * 同时,类中声明变量时,可以写默认值,当默认值存在的时候,构造方法里可以不写
 */
//val person: Person = Person("LC")
val person = Person("LCC")
//    Person person = new Person("LC");

/**
 * 枚举,与java不同,可以给枚举写属性,并且在枚举类中,可以写方法
 */
enum class Color(val r: Int, val g: Int, val b: Int) {
    RED(255, 0, 0),
    ORANGE(255, 165, 0),
    YELLOW(255, 255, 0),
    GREEN(0, 255, 255),
    BLUE(0, 0, 255),
    Black(0, 0, 0),
    WHITE(255, 255, 255);

    fun rgb() = (r * 256 + g) * 256 + b
}

/**
 * when的使用形式比switch更简便,少写了很多重复的代码,同时,不需要case,break
 * when可以选择多个对象在同一分支
 */
fun getColorStr(color: Color) = when (color) {
    RED, ORANGE, YELLOW -> "暖色调"
    GREEN -> "中色调"
    BLUE, Black, WHITE -> "冷色调"
}

/**
 * setOf 代表是一个无序的集合,也就c1和c2的顺序无所谓,谁在前都可以
 */
fun mix(c1: Color, c2: Color) = when (setOf(c1, c2)) {
    setOf(RED, YELLOW) -> getColorStr(ORANGE)
    setOf(YELLOW, BLUE) -> getColorStr(GREEN)
    else -> "不知道什么颜色"
}

/**
 * 没有条件的when,可以直接写出类型来判断
 * 与mix方法是等价的,但是没有条件更麻烦
 */
fun mix2(c1: Color, c2: Color) = when {
    (c1 == RED && c2 == YELLOW) || (c1 == YELLOW && c2 == RED) -> getColorStr(ORANGE)
    (c1 == BLUE && c2 == YELLOW) || (c1 == YELLOW && c2 == BLUE) -> getColorStr(GREEN)
    else -> "并不知道是什么颜色"
}

fun mix3() {
    val animal = Animal()
    println("是动物")
//    if (animal is Dog){
//        animal.dig()
//        println("是狗")
//    }
    val dog = animal as Dog
    dog.dig()
}

/**
 * 循环 while循环 do-while循环 和java没有任何区别
 * 区间: 1..100 1到100的区间,包含100 1 until 100 1到100的区间,不包含100
 * kotlin中唯一一种循环的写法就是for(<item> in <elements>)
 * 下面的区间1..100也就是<elements> i就是item
 * 循环可以倒数,downTo就是倒数,step 是步数
 */
fun fizzBuzz(i: Int) = when {
    i % 15 == 0 -> "fizzBuzz"
    i % 5 == 0 -> "Buzz"
    i % 3 == 0 -> "fizz"
    else -> "$i"
}

fun play() {
//    for (i in 1..100) {
//        println(fizzBuzz(i))
//    }

//    for (i in 100 downTo 0 step 2) {
//        println(fizzBuzz(i))
//    }

    val binaryReps = TreeMap<Char, String>()

    for (c in 'A'..'F') {
        val binary = Integer.toBinaryString(c.toInt())//把ASCII码准换为二进制
        binaryReps[c] = binary//根据键c把值存入map
    }

    //迭代map,使用2个参数来循环map,一个代表key,一个代表value
    for ((key, value) in binaryReps) {
        println("$key=$value")
    }
}

fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
fun isNumber(c: Char) = c in '0'..'9'

fun check(c: Char) = when (c) {
    in '0'..'9' -> "这是一个数字"
    in 'a'..'z', in 'A'..'Z' -> "这是一个字母"
    else -> "这可能是个符号"
}

//在kotlin中,一个非空类型,不能直接赋空值,只能添加一个?来表示这个值可以为空
//val aa: Int = null//错的
val aa: Int? = null

/**
 * try,catch,finally try catch可以作为一个表达式来使用,可以使用=等号来代替
 * Int? 代表这个值可空
 */
fun readNumber(reader: BufferedReader) =
    try {
        val line = reader.readLine()
        Integer.parseInt(line)
    } catch (e: NumberFormatException) {
        null
    } finally {
        reader.close()
    }
//正常写法和java没有任何区别
//    try {
//        val line = reader.readLine()
//        return Integer.parseInt(line)
//    } catch (e: NumberFormatException) {
//        return null
//    } finally {
//        reader.close()
//    }

/**
 * 集合
 * 在kotlin中,没有单独的集合,目前使用的都是Java中的标准集合类
 */
val set = setOf(1, 2, 3)
val list = listOf(1, 2, 3)
val map = mapOf(1 to "one", 2 to "two")

val strings = listOf("first", "second", "fourteenth")

val numbers = setOf(1, 14, 2)

/**
 * collection 是所有集合的父类,所以这里的意思是传递一个集合作为参数
 * separator 是分隔符
 * prefix 是在集合前添加的符号
 * postfix 再集合后添加的符号
 */
fun <T> joinToString(
    collection: Collection<T>, separator: String = ",", prefix: String = "", postfix: String = ""
): String {
    val result = StringBuffer(prefix) //在结果前先添加集合前的符号
    for ((index, element) in collection.withIndex()) {//循环或者迭代这个集合,遍历每一个元素
        if (index > 0) result.append(separator)//不用在第一个元素前添加分隔符
        result.append(element)
    }
    result.append(postfix)//在循环完所有元素之后,添加集合后的符号
    return result.toString()
}

/**
 * 扩展函数
 * 可以给别人的类添加方法
 */
//String 需要添加自定义方法的类
//.lastChar() 是我们自己方法的命名,随意命名
//: Char 是当前方法的返回值
// this.指向当前的string
//.get(this.length - 1) 获取最后一个字符的方法
fun String.lastChar123(): Char = this.get(this.length - 1)